/**
 * Copyright (C) 2013 Bohan
 *
 * This file is part of the Java SACNR API.
 *
 * Java SACNR API is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Java SACNR API is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package bohan.sacnr.api;

import bohan.sacnr.exception.ItemNotFoundException;
import java.net.URL;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.ArrayList;

/**
 * This class does not decode HTML entities, you will have to do it yourself.
 * <p>
 * This class is immutable, meaning that if you want to update the data, you
 * will have to create a new instance of it
 * @author Bohan
 */
public final class GeneralStats
{

    private final int playerID;
    private final long updated;
    private final String totalConnectTime, totalAliveTime;
    private final Integer[] houseIDs;
    private final Integer[] businessIDs;
    private final String[] friends;
    private Business[] businesses = null;
    private House[] houses = null;

    public GeneralStats(int playerID) throws IOException
    {
        this.playerID = playerID;
        String data;
        try
        {
            URL page = new URL("http://sacnr.com/pstats/" + playerID + "/General");
            BufferedReader in = new BufferedReader(
                    new InputStreamReader(page.openStream()));

            StringBuilder sb = new StringBuilder(1000);
            while ((data = in.readLine()) != null)
                sb.append(data);
            in.close();
            updated = System.currentTimeMillis();
            data = sb.toString();
        } catch (MalformedURLException e)
        {
            /**
             * This exception should not occur, but in case it does, re-wrap it
             * and throw as a RunTimeException
             */
            throw new RuntimeException("MalformedURLException: " + e.getMessage(), e);
        }


        if (data.indexOf("Invalid user") >= 0)
            throw new ItemNotFoundException("The player with id "
                    + playerID + " was not found.");

        int index;

        if ((index = data.indexOf("Total connect time:</th><td>") + 28) >= 28)
            totalConnectTime = data.substring(index, data.indexOf('<', index));
        else
            throw new IllegalArgumentException(Player.EXCEPTION_MESSAGE);

        if ((index = data.indexOf("Total alive time:</th><td>", index) + 26) >= 26)
            totalAliveTime = data.substring(index, data.indexOf('<', index));
        else
            throw new IllegalArgumentException(Player.EXCEPTION_MESSAGE);

        if ((index = data.indexOf("<a href=\"http://www.sacnr.com/house/", index) + 36) >= 36)
        {
            ArrayList<Integer> list = new ArrayList<Integer>(3);
            while (index >= 1)
            {
                list.add(Integer.parseInt(data.substring(index, data.indexOf('/', index))));
                int secondIndex = data.indexOf("<a href=\"http://www.sacnr.com/house/", index);
                if (secondIndex < 0)
                    break;
                index = secondIndex += 36;
            }
            houseIDs = list.toArray(new Integer[0]);
        } else
            houseIDs = new Integer[0];

        if ((index = data.indexOf("<a href=\"http://www.sacnr.com/business/", index) + 39) >= 39)
        {
            ArrayList<Integer> list = new ArrayList<Integer>(3);
            while (index >= 1)
            {
                list.add(Integer.parseInt(data.substring(index, data.indexOf('/', index))));
                int secondIndex = data.indexOf("<a href=\"http://www.sacnr.com/business/", index);
                if (secondIndex < 0)
                    break;
                index = secondIndex += 39;
            }
            businessIDs = list.toArray(new Integer[0]);
        } else
            businessIDs = new Integer[0];

        if ((index = data.indexOf("<a href=\"http://www.sacnr.com/u/", index) + 35) >= 35)
        {
            ArrayList<String> list = new ArrayList<String>(10);
            index = data.indexOf('>', index) + 1;
            while (index >= 1)
            {
                list.add(data.substring(index, data.indexOf("<", index)));
                int secondIndex = data.indexOf("<a href=\"http://www.sacnr.com/u/", index);
                if (secondIndex < 0)
                    break;
                index = data.indexOf('>', secondIndex) + 1;
            }
            friends = list.toArray(new String[0]);
        } else
            friends = new String[0];
    }
    
    /**
     * 
     * @return The time since this player's general stats was last updated in 
     * milliseconds
     */
    public long getAge()
    {
        return System.currentTimeMillis() - updated;
    }
    
    public String getTotalConnectTime()
    {
        return this.totalConnectTime;
    }
    
    public String getTotalAliveTime()
    {
        return this.totalAliveTime;
    }
    
    public int getPlayerID()
    {
        return this.playerID;
    }
    
    public Integer[] getHouseIDs()
    {
        return this.houseIDs;
    }
    
    public Integer[] getBusinessIDs()
    {
        return this.businessIDs;
    }
    
    public String[] getFriends()
    {
        return this.friends;
    }
    
    /**
     * Gets an array of <code>Business</code> objects that this player owns.
     * <p>
     * Expensive on the first call, subsequent calls are cheap for each
     * instance of <code>GeneralStats</code>
     * @return An array of <code>Business</code> objects.
     * @throws IOException Thrown from Business constructor when it can't
     * connect to the server
     * @see Business
     */
    public Business[] getBusinesses() throws IOException
    {
        if (businesses == null)
        {
            businesses = new Business[businessIDs.length];
            for (int i = 0; i < businessIDs.length; i++)
                businesses[i] = new Business(businessIDs[i]);
        }
        return businesses;
    }
    
    /**
     * Gets an array of <code>House</code> objects that this player owns.
     * <p>
     * Expensive on the first call, subsequent calls are cheap for each
     * instance of <code>GeneralStats</code>
     * @return An array of <code>House</code> objects.
     * @throws IOException Thrown from House constructor when it can't
     * connect to the server
     * @see House
     */
    public House[] getHouses() throws IOException
    {
        if (houses == null)
        {
            houses = new House[houseIDs.length];
            for (int i = 0; i < houseIDs.length; i++)
                houses[i] = new House(houseIDs[i]);
        }
        return houses;
    }
    
    /**
     * Creates a new instance of <code>GeneralStats</code> object with updated
     * general stats *EXPENSIVE*.
     * <p>
     * Note that since <code>GeneralStats</code> is an immutable class,
     * this method does not update the current <code>GeneralStats</code>
     * instance, but returns a new updated instance of 
     * <code>GeneralStats</code> object. 
     * <p>
     * Note that this method is always expensive, as it connects to the sacnr 
     * website to update the player's general stats.
     * @return An up to date <code>GeneralStats</code> object.
     * @throws IOException Thrown from the <code>GeneralStats</code>
     * constructor when it can't connect to the server.
     * @see GeneralStats#GeneralStats(int) 
     */
    public GeneralStats update() throws IOException
    {
        return new GeneralStats(playerID);
    }
}